/*
 * Copyright (c) 2011-2013, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.alg.misc;

import boofcv.misc.AutoTypeImage;
import boofcv.misc.CodeGeneratorBase;

import java.io.FileNotFoundException;


/**
 * Generates functions inside of {@link ImageMiscOps}.
 *
 * @author Peter Abeles
 */
public class GenerateImageStatistics extends CodeGeneratorBase {

	String className = "ImageStatistics";

	private AutoTypeImage input;

	public void generate() throws FileNotFoundException {
		printPreamble();
		printAll();
		out.println("}");
	}

	private void printPreamble() throws FileNotFoundException {
		setOutputFile(className);
		out.print("import boofcv.struct.image.*;\n" +
				"\n" +
				"/**\n" +
				" * Computes statistical properties of pixels inside an image.\n" +
				" *\n" +
				" * <p>DO NOT MODIFY: Generated by {@link "+getClass().getName()+"}</p>.\n"+
				" *\n"+
				" * @author Peter Abeles\n" +
				" */\n" +
				"public class "+className+" {\n\n");
	}

	public void printAll() {
		AutoTypeImage types[] = AutoTypeImage.getSpecificTypes();

		for( AutoTypeImage t : types ) {
			input = t;
			printMin();
			printMax();
			printMaxAbs();
			printSum();
			printMean();
			printVariance();
			printMeanDiffSq();
			printMeanDiffAbs();
			printHistogram();
		}
	}

	public void printHistogram() {
		if( input.isSigned() ) {

			out.print("\t/**\n" +
					"\t * Computes the histogram of intensity values for the image.\n" +
					"\t * \n" +
					"\t * @param input (input) Image.\n" +
					"\t * @param minValue (input) Minimum possible intensity value   \n" +
					"\t * @param histogram (output) Storage for histogram. Number of elements must be equal to max value.\n" +
					"\t */\n" +
					"\tpublic static void histogram( "+input.getSingleBandName()+" input , int minValue , int histogram[] ) {\n" +
					"\t\tfor( int i = 0; i < histogram.length; i++ )\n" +
					"\t\t\thistogram[i] = 0;\n" +
					"\t\t\n" +
					"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
					"\t\t\tint index = input.startIndex + y*input.stride;\n" +
					"\t\t\tint end = index + input.width;\n" +
					"\n" +
					"\t\t\tfor( ; index < end; index++ ) {\n" +
					"\t\t\t\t// floor value. just convert to int rounds towards zero\n");
			if( input.isInteger()) {
				if( input.getNumBits() == 64 )
					out.print("\t\t\t\thistogram[(int)input.data[index] - minValue]++;\n");
				else
					out.print("\t\t\t\thistogram[input.data[index] - minValue ]++;\n");
			} else
				out.print("\t\t\t\thistogram[(int)input.data[index] - minValue ]++;\n");
			out.print("\t\t\t}\n" +
					"\t\t}\n" +
					"\t}\n\n");
		} else {
			out.print("\t/**\n" +
					"\t * Computes the histogram of intensity values for the image.\n" +
					"\t * \n" +
					"\t * @param input (input) Image.\n" +
					"\t * @param histogram (output) Storage for histogram. Number of elements must be equal to max value.\n" +
					"\t */\n" +
					"\tpublic static void histogram( "+input.getSingleBandName()+" input , int histogram[] ) {\n" +
					"\t\tfor( int i = 0; i < histogram.length; i++ )\n" +
					"\t\t\thistogram[i] = 0;\n" +
					"\t\t\n" +
					"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
					"\t\t\tint index = input.startIndex + y*input.stride;\n" +
					"\t\t\tint end = index + input.width;\n" +
					"\n" +
					"\t\t\tfor( ; index < end; index++ ) {\n" +
					"\t\t\t\thistogram[input.data[index]"+input.getBitWise()+"]++;\n" +
					"\t\t\t}\n" +
					"\t\t}\n" +
					"\t}\n\n");
		}
	}

	public void printMaxAbs() {
		out.print("\t/**\n" +
				"\t * Returns the absolute value of the element with the largest absolute value.\n" +
				"\t * \n" +
				"\t * @param input Input image. Not modified.\n" +
				"\t * @return Largest pixel absolute value.\n" +
				"\t */\n" +
				"\tpublic static "+input.getSumType()+" maxAbs( "+input.getSingleBandName()+" input ) {\n" +
				"\n" +
				"\t\t"+input.getSumType()+" max = 0;\n" +
				"\n" +
				"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
				"\t\t\tint index = input.startIndex + y*input.stride;\n" +
				"\t\t\tint end = index + input.width;\n" +
				"\n" +
				"\t\t\tfor( ; index < end; index++ ) {\n");
		if( input.isSigned() )
			out.print("\t\t\t\t"+input.getSumType()+" v = Math.abs(input.data[index]);\n");
		else
			out.print("\t\t\t\t"+input.getSumType()+" v = input.data[index]"+input.getBitWise()+";\n");
		out.print("\t\t\t\tif( v > max )\n" +
				"\t\t\t\t\tmax = v;\n" +
				"\t\t\t}\n" +
				"\t\t}\n" +
				"\t\treturn max;\n" +
				"\t}\n\n");
	}

	public void printMax() {
		out.print("\t/**\n" +
				"\t * Returns the maximum element value.\n" +
				"\t * \n" +
				"\t * @param input Input image. Not modified.\n" +
				"\t * @return Maximum pixel value.\n" +
				"\t */\n" +
				"\tpublic static "+input.getSumType()+" max( "+input.getSingleBandName()+" input ) {\n" +
				"\n" +
				"\t\t"+input.getSumType()+" max = input.get(0,0);\n" +
				"\n" +
				"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
				"\t\t\tint index = input.startIndex + y*input.stride;\n" +
				"\t\t\tint end = index + input.width;\n" +
				"\n" +
				"\t\t\tfor( ; index < end; index++ ) {\n");
		out.print("\t\t\t\t"+input.getSumType()+" v = input.data[index] "+input.getBitWise()+";\n");
		out.print("\t\t\t\tif( v > max )\n" +
				"\t\t\t\t\tmax = v;\n" +
				"\t\t\t}\n" +
				"\t\t}\n" +
				"\t\treturn max;\n" +
				"\t}\n\n");
	}

	public void printMin() {
		out.print("\t/**\n" +
				"\t * Returns the minimum element value.\n" +
				"\t * \n" +
				"\t * @param input Input image. Not modified.\n" +
				"\t * @return Minimum pixel value.\n" +
				"\t */\n" +
				"\tpublic static "+input.getSumType()+" min( "+input.getSingleBandName()+" input ) {\n" +
				"\n" +
				"\t\t"+input.getSumType()+" min = input.get(0,0);\n" +
				"\n" +
				"\t\tfor( int y = 0; y < input.height; y++ ) {\n" +
				"\t\t\tint index = input.startIndex + y*input.stride;\n" +
				"\t\t\tint end = index + input.width;\n" +
				"\n" +
				"\t\t\tfor( ; index < end; index++ ) {\n");
		out.print("\t\t\t\t"+input.getSumType()+" v = input.data[index] "+input.getBitWise()+";\n");
		out.print("\t\t\t\tif( v < min )\n" +
				"\t\t\t\t\tmin = v;\n" +
				"\t\t\t}\n" +
				"\t\t}\n" +
				"\t\treturn min;\n" +
				"\t}\n\n");
	}

	public void printSum() {

		String bitWise = input.getBitWise();

		out.print("\t/**\n" +
				"\t * <p>\n" +
				"\t * Returns the sum of all the pixels in the image.\n" +
				"\t * </p>\n" +
				"\t * \n" +
				"\t * @param img Input image. Not modified.\n" +
				"\t */\n" +
				"\tpublic static "+input.getSumType()+" sum( "+input.getSingleBandName()+" img ) {\n" +
				"\n" +
				"\t\tfinal int h = img.getHeight();\n" +
				"\t\tfinal int w = img.getWidth();\n" +
				"\n" +
				"\t\t"+input.getSumType()+" total = 0;\n" +
				"\t\t\n" +
				"\t\tfor (int y = 0; y < h; y++) {\n" +
				"\t\t\tint index = img.getStartIndex() + y * img.getStride();\n" +
				"\t\t\t\n" +
				"\t\t\tint indexEnd = index+w;\n" +
				"\t\t\t// for(int x = 0; x < w; x++ ) {\n" +
				"\t\t\tfor (; index < indexEnd; index++ ) {\n" +
				"\t\t\t\ttotal += img.data[index] "+bitWise+";\n" +
				"\t\t\t}\n" +
				"\t\t}\n" +
				"\t\t\n" +
				"\t\treturn total;\n" +
				"\t}\n\n");
	}

	public void printMean() {
		out.print("\t/**\n" +
				"\t * Returns the mean pixel intensity value.\n" +
				"\t * \n" +
				"\t * @param img Input image.  Not modified.\n" +
				"\t * @return Mean pixel intensity value\n" +
				"\t */\n" +
				"\tpublic static double mean( "+input.getSingleBandName()+" img ) {\n" +
				"\t\treturn sum(img)/(double)(img.width*img.height);\n" +
				"\t}\n\n");
	}

	public void printVariance() {

		String bitWise = input.getBitWise();

		out.print("\t/**\n" +
				"\t * Computes the variance of pixel intensity values inside the image.\n" +
				"\t *\n" +
				"\t * @param img Input image. Not modified.\n" +
				"\t * @param mean Mean pixel intensity value.   \n" +
				"\t * @return Pixel variance   \n" +
				"\t */\n" +
				"\tpublic static double variance( "+input.getSingleBandName()+" img , double mean ) {\n" +
				"\n" +
				"\t\tdouble variance = 0;\n" +
				"\n" +
				"\t\tfor (int y = 0; y < img.height; y++) {\n" +
				"\t\t\tint index = img.getStartIndex() + y * img.getStride();\n" +
				"\n" +
				"\t\t\tint indexEnd = index+img.width;\n" +
				"\t\t\t// for(int x = 0; x < img.width; x++ ) {\n" +
				"\t\t\tfor (; index < indexEnd; index++ ) {\n" +
				"\t\t\t\tdouble d = (img.data[index]"+bitWise+") - mean; \n" +
				"\t\t\t\tvariance += d*d;\n" +
				"\t\t\t}\n" +
				"\t\t}\n" +
				"\n" +
				"\t\treturn variance/(img.width*img.height);\n" +
				"\t}\n\n");
	}

	public void printMeanDiffSq() {

		String imageName = input.getSingleBandName();
		String bitWise = input.getBitWise();
		String sumType = input.getSumType();
		String largeSumType = input.getLargeSumType();

		out.print("\t/**\n" +
				"\t * <p>Computes the mean squared error (MSE) between the two images.</p>\n" +
				"\t *\n" +
				"\t * @param imgA first image. Not modified.\n" +
				"\t * @param imgB second image. Not modified.\n" +
				"\t * @return error between the two images.\n" +
				"\t */\n" +
				"\tpublic static double meanDiffSq("+imageName+" imgA, "+imageName+" imgB ) {\n" +
				"\t\t"+largeSumType+" total = 0;\n" +
				"\n" +
				"\t\tfor (int y = 0; y < imgA.height; y++) {\n" +
				"\t\t\tint indexA = imgA.getStartIndex() + y * imgA.getStride();\n" +
				"\t\t\tint indexB = imgB.getStartIndex() + y * imgB.getStride();\n" +
				"\t\t\tfor (int x = 0; x < imgA.width; x++,indexA++,indexB++) {\n" +
				"\t\t\t\t"+sumType+" difference = (imgA.data[indexA]"+bitWise+")-(imgB.data[indexB]"+bitWise+");\n" +
				"\t\t\t\ttotal += difference*difference;\n" +
				"\t\t\t}\n" +
				"\t\t}\n" +
				"\n" +
				"\t\treturn total / (double)(imgA.width*imgA.height);\n" +
				"\t}\n\n");
	}

	public void printMeanDiffAbs() {

		String imageName = input.getSingleBandName();
		String bitWise = input.getBitWise();
		String sumType = input.getSumType();

		out.print("\t/**\n" +
				"\t * <p>Computes the mean squared error (MSE) between the two images.</p>\n" +
				"\t *\n" +
				"\t * @param imgA first image. Not modified.\n" +
				"\t * @param imgB second image. Not modified.\n" +
				"\t * @return error between the two images.\n" +
				"\t */\n" +
				"\tpublic static double meanDiffAbs("+imageName+" imgA, "+imageName+" imgB ) {\n" +
				"\t\t"+sumType+" total = 0;\n" +
				"\n" +
				"\t\tfor (int y = 0; y < imgA.height; y++) {\n" +
				"\t\t\tint indexA = imgA.getStartIndex() + y * imgA.getStride();\n" +
				"\t\t\tint indexB = imgB.getStartIndex() + y * imgB.getStride();\n" +
				"\t\t\tfor (int x = 0; x < imgA.width; x++,indexA++,indexB++) {\n" +
				"\t\t\t\t"+sumType+" difference = (imgA.data[indexA]"+bitWise+")-(imgB.data[indexB]"+bitWise+");\n" +
				"\t\t\t\ttotal += Math.abs(difference);\n" +
				"\t\t\t}\n" +
				"\t\t}\n" +
				"\n" +
				"\t\treturn total / (double)(imgA.width*imgA.height);\n" +
				"\t}\n\n");
	}

	public static void main( String args[] ) throws FileNotFoundException {
		GenerateImageStatistics gen = new GenerateImageStatistics();
		gen.generate();
	}
}
